package io.yunho.yunho.interview.two.juc;


import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


class MyData{
    int number = 0;
    public void addTo60(){
        this.number=60;
    }
}

class MyDataV{//volatile 可见性保证
    volatile int number = 0;
    public void addTo60(){
        this.number=60;
    }
}

class MyDataA{//volatile 不保证原子性 MyDataA.java===> MyDataA.class===>JVM字节码
    volatile int number = 0;
    //volatile 不保证原子性
    //synchronized 自然可以 但是杀鸡焉用牛刀 太重了
    public void addPlusPlus(){
        number++;
    }
    //解决原子性问题
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addMyAtomic(){
        atomicInteger.getAndIncrement();
    }
}

/**
 * Created by Jason on 2020/4/8.
 */
public class VolatileDemo {
    /**
     *1.验证可见性
     *  1.1 假如int number=0;number 变量之前根本没有添加volatile关键字修饰 没有可见性
     */
    @Test
    public void test1(){
        MyData myData= new MyData();
        MyDataV myDataV = new MyDataV();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName()+"\t update number value:"+ myData.number);
        },"AAA").start();
        //第二个线程就是test线程
        while (myData.number==0){

        }
        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
    /**
     *
     *     1.2 int number=0;number 变量之前根本添加volatile关键字修饰 具备可见性 MyDataV
     */
    @Test
    public void test2(){
        MyDataV myDataV = new MyDataV();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDataV.addTo60();
            System.out.println(Thread.currentThread().getName()+"\t update number value:"+ myDataV.number);
        },"AAAV").start();
        //第二个线程就是test线程
        while (myDataV.number==0){

        }
        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
    /**
     * 2. 验证volatile不保证原子性
     *  2.1原子性是什么？
     *      不可分割、完整性，也即某个线程正在做某个具体业务时，中间不可以被加塞或者被分割，需要整体完整
     *      要么同时成功，要么同时失败
     *  2.2 volatile不保证原子性的案例演示
     *  2.3 why?
     *  2.4如何解决原子性？
     *      * 加synchronized 可以 但是不要杀鸡用牛刀
     *      * 使用我们的juc下的AtomicInteger
     */
    @Test
    public void test3(){
        MyDataA myDataA= new MyDataA();
        for(int i=0;i<20;i++){
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    myDataA.addPlusPlus();
                    myDataA.addMyAtomic();
                }
            },String.valueOf(i)).start();
        }
        //等待20线程完成后，取得最终结果值看是多少
        //基础线程+main线程，大于两个代表还有内部线程未完成，yield 礼让线程
        while(Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t int finally number value :"+myDataA.number );
        System.out.println(Thread.currentThread().getName()+"\t AtomicInteger finally number value :"+myDataA.atomicInteger );
    }
}
